import javax.print.attribute.standard.MediaSize.Other;


/**
 * @author jacobbj
 * 
 * provides a number of methods for evaluating the gameboard state. 
 * Most important are columnValue() which is used as evaluation function in MiniMax. How the evaluation is done will be described further later on. Most of the methods in GameBoard are private and only used internally but some other important public methods exist. 
 * Among these are methods for checking whether a column is full, a winning option etc.
 *  
 * 
 */
public final class GameBoardTool {

	public static final int EMPTY = 0;
	public static final int PLAYER1 = 1;
	public static final int PLAYER2 = 2;

	private static final float EmptyFieldfactor = (float) 0.01;


	public GameBoardTool() {

	}


	public void printValues(GameBoard g, int player ) {

		for (int i =1 ; i <= g.columns ; i++) {

			System.out.println("Value of " + i +" " + this.columnValue(g, i, player, 1));
		}
	}

	public float columnValue(GameBoard g, int col, int player, int minimax) {
		
		float value = 0;

		if (columnFull(g, col)) return 0;

		float down = evalDown(g, col, player);
		if (countDown(g, col,player) >= 3)  return winnerValue(player) * minimax ; // winning column
		else value+= down;

		if ( countSidewards(g, col, player) >= 3) return winnerValue(player) * minimax ; // winning column
		else value+= evalSidewards(g, col, player);

		if ( countLeftDown(g, col, player) == 3) return winnerValue(player) * minimax ; // winning column
		else value+= evalLeftDown(g, col,player);

		if ( countRightDown(g, col, player) == 3) return winnerValue(player) * minimax ; // winning column
		else value+= evalRightDown(g, col,player);

		if (value != 0) value = value/10;

		if (player == 2) return value  * minimax ;
		else return - value * minimax ;

	}

	private int winnerValue(int player) {
		if ( player == PLAYER1 ) return -1;
		if ( player == PLAYER2 ) return 1;
		else return 0;
	}


	public boolean columnFull(GameBoard g, int col) {
		return(g.coinsInColumn[col-1] == g.rows);
	}


	public int countDown(GameBoard g, int col, int player) {

		if (columnFull(g, col) || columnEmpty(g, col) ) return 0; 
		else {
			int count = 0;
			int i = g.coinsInColumn[col-1] -1 ;

			while ( i >= 0 && g.board[col-1][i] == player ) {
				count++;
				i--;		
			}	
			return count;
		}
	}	


	public boolean isWinningColumn(GameBoard g , int col, int player) {

		return  (countDown(g, col, player) == 3) || 
				(countSidewards(g, col, player) >= 3) || 
				(countLeftDown(g, col, player) + (countRightUp(g, col, player) ) >= 3 ) || 
				(countRightDown(g, col, player) + (countLeftUp(g, col, player) ) >= 3 ) ;
	}

	// returns winning column if exists, else 0
	public int winningColumn (GameBoard g , int player) {

		for (int i = 1 ; i <= g.columns ; i++) 
		{
			if ( !columnFull(g,i) && isWinningColumn(g, i, player)) return i;
		} 

		return 0;
	}

	private float evalDown(GameBoard g, int col, int player) {		

		int count = countDown(g, col, player) + 1;	
		int onTop = (g.rows - getCoinsInColumn(g, col)) -1 ; // number of free fields above

		if (count + onTop < 4) return 0; // no possibility of winning

		else // 
			return count + (onTop * EmptyFieldfactor); 
	}


	private float evalLeft(GameBoard g, int col, int player) {	

		int count = countLeft(g, col, player);
		int leftFree = countLeft(g, col-count, EMPTY);

		return count + (leftFree * EmptyFieldfactor);

	}

	private float evalRight(GameBoard g, int col, int player) {	

		int count = countRight(g, col, player);
		int rightFree = countRight(g, col+count, EMPTY);

		return count + (rightFree * EmptyFieldfactor);
	}

	private float evalSidewards(GameBoard g, int col, int player) {

		float bonus = 1;
		int row = getCoinsInColumn(g, col);
		int countLeft = countLeft(g, col, player);
		int freeLeft = countLeft(g, col - countLeft, row, EMPTY);
		int countRight = countRight(g, col, player);
		int freeRight = countRight(g, col + countRight, row, EMPTY);

		if (countLeft + countRight + 1 + freeLeft + freeRight < 4 ) return 0;

		if (freeRight > 0 && freeLeft > 0 ) bonus = (float) 1.1; //empty fileds in both direction better

		return countLeft + countRight + 1 + ( (freeLeft + freeRight) * EmptyFieldfactor * bonus);
	}

	private float evalLeftDown (GameBoard g, int col, int player) {

		float bonus = 1;
		int i = col;
		int j = getCoinsInColumn(g, col) + 1;

		int countDown = 0; 
		int countUp = 0;
		int freeBelow = 0; 
		int freeAbove = 0;

		while (pathLeftDown(i, j)) {

			if ( g.board[i-2][j-2] == player ) countDown++;
			else if (g.board[i-2][j-2] == EMPTY) freeBelow++;	
			i--; j--;
		}

		i = col;
		j = getCoinsInColumn(g, col) + 1;

		while (pathRightUp(g, i, j)) {
			if ( g.board[i][j] == player ) countUp++;
			else if (g.board[i][j] == EMPTY) freeAbove++;	
			i++; j++;
		}


		if (countDown + countUp + freeBelow + freeAbove + 1 < 4 ) return 0;

		if (freeBelow > 0 && freeAbove > 0 ) bonus = (float) 1.1; //empty fields in both direction better

		return (countDown + countUp + 1 + (freeBelow + freeAbove) * EmptyFieldfactor * bonus);

	}

	private float evalRightDown(GameBoard g, int col, int player) {		

		float bonus = 1;
		int i = col;
		int j = getCoinsInColumn(g, col) + 1;

		int countDown = 0; 
		int countUp = 0;
		int freeBelow = 0; 
		int freeAbove = 0;

		while (pathRightDown(g, i, j)) {

			if ( g.board[i][j-2] == player ) countDown++;
			else if (g.board[i][j-2] == EMPTY) freeBelow++;	
			i++; j--;
		}

		i = col;
		j = getCoinsInColumn(g, col) + 1;

		while (pathLeftUp(g, i, j)) {
			if ( g.board[i-2][j] == player ) countUp++;
			else if (g.board[i-2][j] == EMPTY) freeAbove++;	
			i--; j++;
		}


		if (countDown + countUp + freeBelow + freeAbove + 1 < 4 ) return 0;

		if (freeBelow > 0 && freeAbove > 0 ) bonus = (float) 1.1; //empty fields in both direction better

		return (countDown + countUp + 1 + (freeBelow + freeAbove) * EmptyFieldfactor * bonus);

	}

	private int countLeft(GameBoard g, int col, int player) {

		if (columnFull(g, col) ) return 0;
		return countLeft(g, col, g.coinsInColumn[col-1], player);
	}

	private int countLeft(GameBoard g, int col, int row, int player) {

		int count = 0;
		int i = col-1;
		int j = row;

		while ( i > 0 && g.board[i-1][j] == player ) {
			count++;
			i--;		
		}
		return count;
	}

	private int countRight(GameBoard g, int col, int row, int player) {

		int count = 0;
		int i = col;
		int j = row;

		// count on right side
		while ( i < g.columns && g.board[i][j] == player ) {
			count++;
			i++;	
		}

		return count;
	}

	private int countRight (GameBoard g,int col, int player) {

		if (columnFull(g, col) ) return 0;
		return countRight(g, col, g.coinsInColumn[col-1], player);

	}

	private int countSidewards(GameBoard g,int col, int player) {

		return countLeft(g, col, player) + countRight(g, col, player);
	}


	private int countLeftDown(GameBoard g,int col, int player) {

		if (col <= 1) return 0; 

		return countLeftDown(g, col, getCoinsInColumn(g, col) + 1, player);
	}

	private int countLeftDown(GameBoard g, int col, int row, int player) {

		if (col <= 1 || row <= 1) return 0; 


		else {

			int count = 0;
			int i = col-1;
			int j = row-1;

			while ( i > 0 && j> 0 && g.board[i-1][j-1] == player ) {

				count++;
				j--;
				i--;		
			}

			return count;
		}
	}

	private int countLeftUp(GameBoard g, int col, int player) {

		if (columnFull(g, col) || col == 1 ) return 0; 

		else {
			int count = 0;
			int j = g.coinsInColumn[col-1] + 1;
			int i = col-2;

			while ( i >= 0 && j < g.rows && g.board[i][j] == player ) {

				count++;
				j++;
				i--;		
			}

			return count;
		}
	}

	private int countRightUp(GameBoard g, int col, int player) {

		if (columnFull(g,col) || col == g.columns ) return 0; 

		else {
			int count = 0;
			int j = g.coinsInColumn[col-1] + 1;
			int i = col + 1;

			while ( i <= g.columns && j < g.rows && g.board[i-1][j] == player ) {

				count++;
				j++;
				i++;		
			}

			return count;
		}
	}

	private int countRightDown(GameBoard g, int col, int player) {

		if (columnFull(g, col) || columnEmpty(g, col) || col == g.columns ) return 0; 

		else {
			int count = 0;
			int i = g. coinsInColumn[col-1] -1;
			int j = col;

			while ( i >= 0 && j< g.columns && g.board[j][i] == player ) {

				count++;
				j++;
				i--;		
			}

			return count;
		}
	}


	private boolean pathDown(int col, int row) {
		return (row - 1 > 0); 
	}

	private boolean pathUp(GameBoard g, int col, int row) {
		return (g.rows - row > 0);
	}

	private boolean pathLeft(int col, int row) {
		return (col - 1 > 0);
	}

	private boolean pathRight(GameBoard g,int col, int row) {
		return (col + 1 <= g.columns);
	}

	private boolean pathLeftDown(int col, int row) {
		return (pathLeft(col,row) && pathDown(col,row));
	}

	private boolean pathRightDown(GameBoard g, int col, int row) {
		return (pathRight(g, col,row) && pathDown(col,row));
	}

	private  boolean pathLeftUp(GameBoard g, int col, int row) {
		return (pathLeft(col,row) && pathUp(g, col,row));
	}

	private boolean pathRightUp(GameBoard g, int col, int row) {
		return (pathRight(g,col,row) && pathUp(g, col,row));
	}

	private int getCoinsInColumn(GameBoard g, int col) {
		return g.coinsInColumn[col-1];
	}

	private boolean columnEmpty(GameBoard g, int col) {
		return(g.coinsInColumn[col-1] == 0);
	}

}


